package org.fujene.search;

import java.io.IOException;
import org.fujene.communicate.MainService;
import org.fujene.logging.LogParser;
import org.msgpack.MessagePackObject;
import org.msgpack.object.ArrayType;
import org.msgpack.object.IntegerType;
import org.msgpack.object.NilType;
import org.msgpack.object.RawType;
import org.msgpack.rpc.Request;
import org.msgpack.rpc.Server;
import org.msgpack.rpc.loop.EventLoop;

public class SearchService{
	// Work without executor
	public boolean stopFlag = false;
	public volatile int threadCounter = 0;
	private MainService mains = null;
	private int port;
	private Server server;
	private int bFlagSize, mFlagSize;

	public SearchService(MainService mains, int port){
		this.mains = mains;
		this.port = port;
		this.bFlagSize = mains.contentInfo().binaryFlagNum();
		this.mFlagSize = mains.contentInfo().multipleFlagNum();
	}

	public class SearchRPCHandler{
		/** @param req Arguments with shapes {@link ArrayType}, details as follows.<br>
		 *            [0]: {@link String}, Query<br>
		 *            [1]: int, Result start<br>
		 *            [2]: int, Result length<br>
		 *            [3]: boolean, intersect results if true, or join all results<br>
		 *            [4]: int, Target number<br>
		 *            [5]-[...]: {@link String}, Target field name<br>
		 * @throws Exception */
		public void search(Request req) throws Exception{
			MessagePackObject obj = req.getArguments();
			MessagePackObject[] args = obj.asArray();

			if(args.length < 5
				&& (!args[0].isRawType() || !args[1].isIntegerType() || !args[2].isIntegerType()
					|| !args[3].isBooleanType() || !args[4].isIntegerType())){
				LogParser.severe("Please post appropriate query.");
				req.sendError(RawType.create("Please post appropriate query."));
			}

			int searchTargets = args[4].asInt();
			if(searchTargets == 0){
				req.sendResult(RawType.create("Specify target field."));
				return;
			}
			byte[] searchTypes;
			int arrayLen = mains.contentInfo().contentsNum();
			arrayLen = (arrayLen % 8 == 0? arrayLen / 8: arrayLen / 8 + 1);
			searchTypes = new byte[arrayLen];
			int specified, byteindex, bit;

			for(int i = 0; i < searchTargets; i++){
				specified = mains.contentInfo().contentIndex(args[5 + i].asString());
				if(specified == -1){
					req.sendError("Specified field doesn't exist.");
					return;
				}

				byteindex = specified / 8;
				bit = specified % 8;
				searchTypes[byteindex] += (1 << bit);
			}

			try{
				SearchPackage spkg =
					SearchPackage.createPackage(args[0].asString(), args[1].asInt(), args[2].asInt(),
						searchTypes, args[3].asBoolean(), bFlagSize, mFlagSize);
				int index = 5 + searchTargets;
				for(int i = 0; i < (index + bFlagSize > args.length? args.length - index: bFlagSize); i++)
					spkg.setBinaryFlag(i, args[index + i].asByte());

				index += bFlagSize;
				if(index + 2 < args.length)
					spkg.setTimeRange(args[index].asInt(), args[index + 1].asLong(), args[index + 2].asLong());
				index += 3;
				for(int i = index, j = 0; i < (index + (3 * mFlagSize) > args.length? args.length - 2: index
					+ (3 * mFlagSize)); i += 3, j++)
					spkg.setMultipleFlag(j, args[i].asByte(), args[i + 1].asInt(), args[i + 2].asInt());

				SearchResultPackage result = mains.setNewSearch(spkg);
				MessagePackObject[] objs = new MessagePackObject[result.getArraySize() + 1];
				int count = 0;
				int i;
				objs[count++] = IntegerType.create(result.total());
				for(SearchResult eachQueue: result.getResults()){
					for(i = 0; i < eachQueue.contentSize(); i++)
						objs[count++] = RawType.create(eachQueue.content(i));
					for(i = 0; i < eachQueue.appendixSize(); i++)
						objs[count++] = RawType.create(eachQueue.appendix(i));
				}
				req.sendResult(ArrayType.create(objs));
			}catch(Exception e){
				e.printStackTrace();
			}
		}

		public void argument(Request req){
			int contentVal = mains.contentInfo().contentsNum();
			int binaryVal = mains.contentInfo().binaryFlagNum();
			int multiVal = mains.contentInfo().multipleFlagNum();

			MessagePackObject[] objs = new MessagePackObject[3 + contentVal];
			objs[0] = IntegerType.create(6 + binaryVal + (multiVal * 3));
			objs[1] = IntegerType.create(binaryVal);
			objs[2] = IntegerType.create(multiVal);
			for(int i = 0; i < contentVal; i++)
				objs[3 + i] = RawType.create(mains.contentInfo().contentName(i));

			req.sendResult(ArrayType.create(objs));
		}

		public void stop(Request req){
			this.notifyAll();
			req.sendResult(NilType.create());
		}
	}

	public void initService(){
		EventLoop eventLoop = EventLoop.defaultEventLoop();
		try{
			server = new Server(eventLoop);
			server.serve(new SearchRPCHandler());
			server.listen(port);
		}catch(IOException e){}
	}

	public void stopService(){
		server.close();
	}
}
